using System;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Description;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Microsoft.Practices.ServiceLocation;

namespace Caramel.ServiceModel
{
	public class ServiceLocatorInstanceProviderServiceBehavior : IServiceBehavior
	{
		#region IServiceBehavior Members

		public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
		{
		}

		public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
		{
			var serviceBehavior = 
				serviceDescription.
					Behaviors.
					Find<ServiceBehaviorAttribute>();

			var isSingleInstance = 
				(serviceBehavior != null) && 
				(serviceBehavior.InstanceContextMode == InstanceContextMode.Single);
			
			var contracts = new Dictionary<string, Type>();

			var endpoints =
				serviceDescription.
					Endpoints.
					Where(e => !e.IsSystemEndpoint);

			foreach (var endpoint in endpoints)
			{
				contracts.Add(endpoint.Contract.Name, endpoint.Contract.ContractType);
			}

			foreach (var channelDispatcher in serviceHostBase.ChannelDispatchers.OfType<ChannelDispatcher>().Cast<ChannelDispatcher>())
			{
				foreach (var endpoint in channelDispatcher.Endpoints.Where(e => contracts.ContainsKey(e.ContractName)))
				{
					if (isSingleInstance)
					{
						endpoint.
							DispatchRuntime.
							SingletonInstanceContext = 
								new InstanceContext(serviceHostBase);
					}

					endpoint.
						DispatchRuntime.
						InstanceProvider =
							new ServiceLocatorInstanceProvider(contracts[endpoint.ContractName]);
				}
			}
		}

		public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
		{
		}

		#endregion
	}
}
